"""
仿射模型使用逻辑回归(logistic regression)
本例用简单的仿射模型进行数据推理后,加入logistic regression
数据矩阵中,每行作为1个样本,列表示1个样本具有的feature
f(x)=x*w+b
y_hat=1/(1+e^-x),这里的x是f(x)计算出的结果,将f(x)映射到0~1的范围
使用二分类交叉熵(Binary Cross Entropy)计算损失,torch中使用torch.nn.BCELoss
loss=-(y*logy_hat+(1-y)*log(1-y_hat))

对各个api有疑问的可以看LinearModel_LinearRegression_Torch.py
"""

import torch

# 构建数据集
x_data = torch.Tensor([[1], [2], [3]])
# 二分法,对应在1/2/3的情况下,分别对应的值(0/1)
y_data = torch.Tensor([[0], [0], [1]])


class LogisticRegressionModel(torch.nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        # 定义线性模型f(x)=x*w+b,
        self.linear = torch.nn.Linear(in_features=1, out_features=1)

    def forward(self, x):
        # 先求出f(x)
        fx = self.linear(x)
        # 对fx进行sigmoid求取,sigmoid其实有多种函数求取方式,这里默认为logistic regression
        y_pred = torch.sigmoid(fx)
        return y_pred


# 初始化模型
model = LogisticRegressionModel()
# 设置损失,使用二分类交叉熵(Binary Cross Entropy)
criterion = torch.nn.BCELoss(reduction='sum')
# criterion = torch.nn.CrossEntropyLoss()
# 设置优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 训练
for epoch in range(1000):
    # 计算前馈(并使用sigmoid(logistic regression)映射到0~1)
    y_pred = model(x_data)
    # 计算损失
    loss = criterion(y_pred, y_data)
    print(epoch, loss.item())
    # 先将以前的梯度置零
    optimizer.zero_grad()
    # 求取反向传播,此时会计算出梯度
    loss.backward()
    # 使用设置的学习率和已经计算的梯度更新权重
    optimizer.step()

print(model(torch.Tensor([4.0])).item())
